/*
 * Copyright 2010 Christoph Widulle (christoph.widulle@googlemail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */



package com.android1.amarena2d.nodes.layer;

import com.android1.amarena2d.animation.Animation;
import com.android1.amarena2d.annotations.Incomplete;
import com.android1.amarena2d.engine.AmarenaCamera;
import com.android1.amarena2d.engine.GL;
import com.android1.amarena2d.nodes.LayerLeaf;
import com.android1.amarena2d.nodes.sprites.SharedSprite;
import com.android1.amarena2d.texture.ManagedTexture;
import com.android1.amarena2d.texture.TextureFrame;
import com.android1.amarena2d.texture.TexturedMesh;
import com.badlogic.gdx.math.Vector2;


//todo change to be a entitycontainer?
//todo improve speed
@Incomplete
public class ScrollableRepeatingBackground extends LayerLeaf {

    protected boolean repeatX = true;
    protected boolean repeatY = true;

    protected float startx;
    protected float starty;
    protected float endx;
    protected float endy;

    protected AmarenaCamera camera;

    private boolean dirty;

    protected CustomSprite sprite;

    protected final Vector2 lastPos = new Vector2();


    public ScrollableRepeatingBackground(ManagedTexture managedTexture) {
        this.sprite = new CustomSprite(managedTexture);

    }

    public ScrollableRepeatingBackground(String texturePath) {
        this(engine.getTextureManager().getTexture(texturePath));
    }

    public ScrollableRepeatingBackground(TexturedMesh sharedTexturedMesh) {
        this.sprite = new CustomSprite(sharedTexturedMesh);
    }

    public ScrollableRepeatingBackground(TextureFrame textureFrame) {
        this.sprite = new CustomSprite(textureFrame);
    }

    @Override
    public void init() {
        this.camera = engine.getCamera();
        sprite.init();
        dirty = true;

        super.init();
    }


    @Override
    public void enter() {
        super.enter();
        sprite.enter();
    }

    @Override
    public void exit() {
        super.exit();
        sprite.exit();
    }

    @Override
    public void dispose() {
        super.dispose();
        sprite.dispose();
    }

    @Override
    public void render() {
        if (!init || !active || !visible)
            return;
        //no culling here as it does not make sense.

        final float pX = sprite.getX();
        final float pY = sprite.getY();

        if (lastPos.x != pX || lastPos.y != pY) {
            lastPos.x = pX;
            lastPos.y = pY;
            dirty = true;
        }


        GL.pushMatrix();
        GL.setColor(this.color);
        GL.blendFunc(sprite.getBlendFunction());

        if (transformDelegate != null) {
            transformDelegate.apply();
        }

        final float width = sprite.getWidth();
        final float height = sprite.getHeight();

        if (dirty) {
            if (repeatX) {
                startx = findStartX();
                endx = findEndX(startx);
            }
            if (repeatY) {
                starty = findStartY();
                endy = findEndY(starty);
            }
            dirty = false;
        }

        if (repeatX && repeatY) {
            float x = startx;
            float y = starty;

            while (!(x > endx)) {
                while (!(y > endy)) {
                    render(x, y);
                    y += height;
                }
                x += width;
                y = starty;
            }
        } else if (repeatX && !repeatY) {
            float x = startx;
            final float y = getY();

            while (!(x > endx)) {
                render(x, y);
                x += width;
            }
        } else if (repeatY && !repeatX) {
            final float x = getX();
            float y = starty;

            while (!(y > endy)) {
                render(x, y);
                y += height;
            }
        }
        GL.popMatrix();
    }

    private void render(final float x, final float y) {
        GL.translate(x, y);
        sprite.render();
        GL.translate(-x, -y);
    }

    private float findStartX() {
        float currentMinX = sprite.getAbsoluteMinX();
        float currentMaxX = sprite.getAbsoluteMaxX();
        final float width = sprite.getWidth();
        final float left = camera.getLeft();

        if (currentMaxX < left) {
            while (currentMaxX < left) {
                currentMaxX += width;
            }
            return currentMaxX - width;

        } else if (currentMinX > left) {
            while (currentMinX > left) {
                currentMinX -= width;
            }
            return currentMinX;

        } else {
            return currentMinX;
        }
    }

    private float findEndX(final float startX) {

        final float width = sprite.getWidth();
        float maxX = startX + width;
        final float right = camera.getRight();

        if (maxX < right) {
            while (maxX < right) {
                maxX += width;
            }
            return maxX - width;
        } else {
            return startX;
        }
    }

    private float findStartY() {
        float currentMinY = sprite.getAbsoluteMinY();
        float currentMaxY = sprite.getAbsoluteMaxY();
        final float height = sprite.getHeight();
        final float bottom = camera.getBottom();

        if (currentMaxY < bottom) {
            while (currentMaxY < bottom) {
                currentMaxY += height;
            }
            return currentMaxY - height;

        } else if (currentMinY > bottom) {
            while (currentMinY > bottom) {
                currentMinY -= height;
            }
            return currentMinY;

        } else {
            return currentMinY;
        }
    }

    private float findEndY(final float startY) {

        final float height = sprite.getHeight();
        float maxY = startY + height;
        final float top = camera.getTop();

        if (maxY < top) {
            while (maxY < top) {
                maxY += height;
            }
            return maxY - height;
        } else {
            return startY;
        }
    }


    @Override
    public void setXY(float x, float y) {
        sprite.setXY(x, y);
        dirty = true;
    }

    @Override
    public void setX(float x) {
        sprite.setX(x);
        dirty = true;
    }

    @Override
    public void setY(float y) {
        sprite.setY(y);
        dirty = true;
    }

    @Override
    public void setOffset(Vector2 offset, float scale) {
        sprite.setOffset(offset, scale);
        dirty = true;
    }

    @Override
    public void setOffset(Vector2 offset) {
        sprite.setOffset(offset);
        dirty = true;
    }

    public boolean isRepeatY() {
        return repeatY;
    }

    public boolean isRepeatX() {
        return repeatX;
    }

    public void setRepeatX(boolean repeatX) {
        this.repeatX = repeatX;
        this.dirty = true;
    }

    public void setRepeatY(boolean repeatY) {
        this.repeatY = repeatY;
        this.dirty = true;
    }

    @Override
    public void addXY(float x, float y) {
        sprite.addXY(x, y);
        dirty = true;
    }

    @Override
    public void addX(float x) {
        sprite.addX(x);
        dirty = true;
    }

    @Override
    public void addY(float y) {
        sprite.addY(y);
        dirty = true;
    }

    @Override
    public void setDisableCulling(boolean disableCulling) {
        throw new UnsupportedOperationException("Not available. Will always be displayed.");
    }


    private static class CustomSprite extends SharedSprite {

        public CustomSprite(TexturedMesh sharedTexturedMesh) {
            super(sharedTexturedMesh);
        }

        public CustomSprite(TexturedMesh sharedTexturedMesh, float x, float y) {
            super(sharedTexturedMesh, x, y);
        }

        public CustomSprite(TextureFrame textureFrame) {
            super(textureFrame);
        }

        public CustomSprite(TextureFrame textureFrame, float x, float y) {
            super(textureFrame, x, y);
        }

        public CustomSprite(ManagedTexture managedTexture) {
            super(managedTexture);
        }

        public CustomSprite(ManagedTexture managedTexture, float x, float y) {
            super(managedTexture, x, y);
        }

        public CustomSprite(String texturePath) {
            super(texturePath);
        }

        public CustomSprite(String texturePath, float x, float y) {
            super(texturePath, x, y);
        }

        public CustomSprite(Animation animation) {
            super(animation);
        }

        public CustomSprite(Animation animation, float x, float y) {
            super(animation, x, y);
        }

        @Override
        public void render() {
            texturedMesh.render();
        }
    }

}